import Taro from '@tarojs/taro';

// 请求方法类型
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';

// 基础配置类型
type RequestConfig = {
  baseURL?: string;
  timeout?: number;
  headers?: Record<string, string>;
};

// 单个请求选项（D为请求参数类型）
type RequestOptions<D = unknown> = {
  url: string;
  method?: HttpMethod;
  data?: D;
  headers?: Record<string, string>;
  timeout?: number;
  skipToken?: boolean;
};

// 后端响应通用结构（T为响应数据类型）
type ApiResponse<T = unknown> = {
  code: number;
  message: string;
  data: T;
};

// 错误类型枚举
export enum RequestErrorType {
  NETWORK_ERROR = 'NETWORK_ERROR',
  TIMEOUT_ERROR = 'TIMEOUT_ERROR',
  BUSINESS_ERROR = 'BUSINESS_ERROR',
  UNKNOWN_ERROR = 'UNKNOWN_ERROR',
}

// 错误对象类型（明确 response 类型范围）
export type RequestError = {
  message: string;
  type: RequestErrorType;
  code?: number;
  response?: Taro.request.SuccessCallbackResult | Error; // 严格限制 response 类型
};

// 类型守卫：判断是否为RequestError
export const isRequestError = (error: unknown): error is RequestError => {
  return (
    typeof error === 'object' &&
    error !== null &&
    'message' in error &&
    typeof error.message === 'string' &&
    'type' in error &&
    Object.values(RequestErrorType).includes(error.type as RequestErrorType)
  );
};

// 创建错误对象（泛型 R 约束为允许的 response 类型）
const createError = <R extends Taro.request.SuccessCallbackResult | Error | undefined>(
  message: string,
  type: RequestErrorType,
  code?: number,
  response?: R
): RequestError => ({
  message,
  type,
  code,
  response,
});

// 请求拦截器（泛型约束请求参数类型）
const requestInterceptor = async <D = unknown>(
  options: RequestOptions<D>,
  config: RequestConfig
): Promise<RequestOptions<D>> => {
  const fullUrl = options.url.startsWith('http')
    ? options.url
    : `${config.baseURL || ''}${options.url}`;

  const headers = { ...config.headers, ...options.headers };

  if (!options.skipToken) {
    const token = Taro.getStorageSync<string>('token');
    if (token) {
      headers.Authorization = `Bearer ${token}`;
    }
  }

  return {
    ...options,
    url: fullUrl,
    headers,
    timeout: options.timeout || config.timeout,
  };
};

// 响应拦截器（泛型约束响应数据类型）
const responseInterceptor = async <T = unknown>(
  response: Taro.request.SuccessCallbackResult
): Promise<T> => {
  const { statusCode, data } = response;

  if (statusCode < 200 || statusCode >= 300) {
    throw createError(
      `HTTP错误: ${statusCode}`,
      RequestErrorType.NETWORK_ERROR,
      statusCode,
      response // response 是 Taro.request.SuccessCallbackResult，符合约束
    );
  }

  const apiData = data as ApiResponse<T>;
  if (apiData.code !== 200) {
    if (apiData.code === 401) {
      Taro.removeStorageSync('token');
      Taro.navigateTo({ url: '/pages/login/index' });
    }
    throw createError(
      apiData.message || `业务错误: ${apiData.code}`,
      RequestErrorType.BUSINESS_ERROR,
      apiData.code,
      response // 符合约束
    );
  }

  return apiData.data;
};

// 核心请求函数创建器
const createRequest = (config: RequestConfig = {}) => {
  const baseConfig: Required<RequestConfig> = {
    baseURL: '',
    timeout: 10000,
    headers: { 'Content-Type': 'application/json' },
    ...config,
  };

  // 通用请求方法（T：响应数据类型，D：请求参数类型）
  const request = async <T = unknown, D = unknown>(
    options: RequestOptions<D>
  ): Promise<T> => {
    try {
      const processedOptions = await requestInterceptor<D>(options, baseConfig);
      const response = await Taro.request({
        url: processedOptions.url,
        method: processedOptions.method || 'GET',
        data: processedOptions.data,
        header: processedOptions.headers,
        timeout: processedOptions.timeout,
      });
      return await responseInterceptor<T>(response);

    } catch (error) {
      if (isRequestError(error)) {
        throw error;
      }

      if (error instanceof Error) {
        const isTimeout = error.message.includes('timeout');
        throw createError<Error>( // 显式指定 R 为 Error，符合约束
          isTimeout ? '请求超时' : '网络错误',
          isTimeout ? RequestErrorType.TIMEOUT_ERROR : RequestErrorType.NETWORK_ERROR,
          undefined,
          error
        );
      }

      // 未知错误处理（兜底转换为 Error 类型）
      const unknownError = new Error('未知错误');
      throw createError<Error>(
        '未知错误',
        RequestErrorType.UNKNOWN_ERROR,
        undefined,
        unknownError
      );
    }
  };

  // 快捷请求方法
  return {
    request,
    get: <T = unknown, D = unknown>(options: Omit<RequestOptions<D>, 'method'>) =>
      request<T, D>({ ...options, method: 'GET' }),
    post: <T = unknown, D = unknown>(options: Omit<RequestOptions<D>, 'method'>) =>
      request<T, D>({ ...options, method: 'POST' }),
    put: <T = unknown, D = unknown>(options: Omit<RequestOptions<D>, 'method'>) =>
      request<T, D>({ ...options, method: 'PUT' }),
    delete: <T = unknown, D = unknown>(options: Omit<RequestOptions<D>, 'method'>) =>
      request<T, D>({ ...options, method: 'DELETE' }),
  };
};

// 初始化请求实例
export const request = createRequest({
  baseURL: 'https://api.example.com',
  timeout: 15000,
  headers: {
    'X-Platform': Taro.getEnv() as string,
  },
});